package com.clay.appointment.Filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.clay.appointment.Utils.TokenUtils;
import com.clay.appointment.exception.ExceptionEnum;
import com.clay.appointment.exception.GlobalException;
import com.clay.appointment.response.Result;
import com.clay.appointment.response.ResultEnum;
import com.clay.appointment.vo.LoginVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.util.Map;

/**
 * @Author : clay
 * @Description:
 * @Date: Create in 9:56 2021/8/30
 * @Modified By:
 */
@Slf4j
@Component
public class TokenFilter implements GlobalFilter, Ordered {

    public final static String ATTRIBUTE_IGNORE_TEST_GLOBAL_FILTER = "@IgnoreGlobalFilter";
    private static final long REDIS_TIMEOUT_CHECK_TIME = 60 * 25;
    private static final long REDIS_TIMEOUT = 30 * 60;
    private static final String IP = "ip";
    private static final String USER_ID = "userId";
    /**
     * 超时
     */

    private static final String REDIS_HAS_TIMEOUT = "token已经超时,请重新登录";

    /**
     * 校验成功
     */
    private static final String SUCCESS = "校验成功";
    /**
     * 校验失败
     */
    private static final String FAILED = "校验失败";


    @Value(value = "${token.name}")
    private String tokenName;

    @Resource
    private TokenUtils tokenUtils;

    @Resource
    private RedisTemplate<String, LoginVo> redisTemplate;


    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        if (exchange.getAttribute(ATTRIBUTE_IGNORE_TEST_GLOBAL_FILTER) != null) {
            log.info("url地址" + exchange.getRequest().getURI());
            return chain.filter(exchange);
        }

        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();

        //获取请求头
        if ( request.getHeaders().get("token").isEmpty()) {
            GlobalException.throwOut(ExceptionEnum.TOKEN_MISSION);
        }
        String token = request.getHeaders().get("token").get(0);
        //检查token是否存在
        if (StringUtils.isBlank(token)) {
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return getVoidMono(response, ExceptionEnum.TOKEN_MISSION);
        }
        //解密
        Map<String, String> decode = tokenUtils.decode(token, exchange);
        String verify = tokenUtils.verify(token);
        response.getHeaders().add("Content-Type", "application/json; charset=utf-8");
        if (verify.equals(FAILED)) {
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            response.bufferFactory().wrap(ExceptionEnum.AUTH_FAILED.toString().getBytes());
        } else if (verify.equals(REDIS_HAS_TIMEOUT)) {
            response.setStatusCode(HttpStatus.REQUEST_TIMEOUT);
            response.bufferFactory().wrap(ExceptionEnum.TOKEN_EXPIRED.toString().getBytes());
        }
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return 10;
    }

    private Mono<Void> getVoidMono(ServerHttpResponse serverHttpResponse, ExceptionEnum exceptionEnum) {
        serverHttpResponse.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        Result responseResult = Result.error(exceptionEnum);
        DataBuffer dataBuffer = serverHttpResponse.bufferFactory().wrap(JSON.toJSONString(responseResult).getBytes());
        return serverHttpResponse.writeWith(Flux.just(dataBuffer));
    }


    /**
     * 拦截并返回自定义的json字符串
     */
    private Mono<Void> denyAccess(ServerWebExchange exchange, ResultEnum resultEnum) {
        ServerHttpResponse response = exchange.getResponse();
        response.setStatusCode(HttpStatus.OK);
        //这里在返回头添加编码，否则中文会乱码
        response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        byte[] bytes = JSON.toJSONBytes(Result.error(resultEnum), SerializerFeature.WriteMapNullValue);
        DataBuffer buffer = response.bufferFactory().wrap(bytes);
        return response.writeWith(Mono.just(buffer));
    }
}
